# Spy sappin my imports!
import subprocess
import calendar
import re
import sys
import threading
import time
import urllib2

# Initial Variable Values
optoken = open("optoken.txt").read().strip()
cneeded = False
opened = False
debug = False
lconf = 0
lopen = 0
lbeat = 0
players = []
chatlog = ["Server restarted."]
beats = [0]
fbeat = time.time()
closeit = time.time() * time.time() * time.time()

# Reset admins.txt (used as swap for custom, admins2.txt is the real deal)
open("admins.txt","w").write("")

# Server STDin Thread
class StdinThread(threading.Thread):
    IDENTIFIER = "StdinPlugin"
    def __init__(self, server):
        threading.Thread.__init__(self)
        self.server = server
        self.stop = False
    def run(self):
        while not self.stop:
            line = sys.stdin.readline()
            if line:
                self.server.stdin.write(line)
            if self.stop:
                break
            time.sleep(0.1)
            
# For logging
log = open("minecraft.log",'a')

# RegEx for Logging In, Chatting, Admining, and Disconnecting.  Cool :).
loginregexp = re.compile("\d\d:\d\d:\d\d  \/\d+\.\d+\.\d+\.\d+ logged in as ([\w\d\.]+)")
disconnectregexp = re.compile("\d\d:\d\d:\d\d  ([\w\d\.]+) \(\/\d+\.\d+\.\d+\.\d+\) disconnected")
adminregexp = re.compile("\d\d:\d\d:\d\d  ([\w\d\.]+) admins: (.*)")
chatregexp = re.compile("\d\d:\d\d:\d\d  ([\w\d\.]+) says: (.*)")      
muteregexp = re.compile("\d\d:\d\d:\d\d  Muting ([\w\d\.]+) for chatting too much")                   

# Definin' Funkshins

# Close this place DOWN.
def close():
    if debug:
       print "close detected"
    server.stdin.write(optoken+"say *&4Server CLOSED to free users.\n")
    server.stdin.write(optoken+"say *&4Free users have 60 seconds to use !freetrust\n")
    server.stdin.write(optoken+"say *&4Type !help freetrust to see usage information.\n")

# Print out a nice split-string

def coolList(list, color, token, z):
    swapList = []
    theList = []
    for x in list:
	if len(", ".join(swapList))+len(x) > z:
            theList.append(swapList)
            swapList = [x]
        else:
            swapList.append(x)
    theList.append(swapList)
    for x in theList:
	server.stdin.write(optoken+"say "+token+color+", ".join(x)+"\n")	


# Is he an admin?
def isadmin(username):
    if username.lower() in open('admins2.txt').read().splitlines():
	return True
    return False

# Checks for redditor / premium-ship
def isredditor(username):
    if username.lower() in open('admins2.txt').read().splitlines():
	return "admin"
    elif urllib2.urlopen(urllib2.Request("http://minecraft.net/haspaid.jsp?user="+username.lower())).read().strip() == "true":
	return "premium"
    else:
	if username.lower() in open("redditors.txt").read().splitlines() or username.lower() in open("reddit2.txt").read().splitlines():
	    return "reddit"
        response = str(urllib2.urlopen(urllib2.Request("http://www.reddit.com/r/Minecraft/comments/bd89e/reddit_public_access_thread/.mobile")).read()).lower()
        if '<a href="/user/'+username.lower() in response:
            response = str(urllib2.urlopen(urllib2.Request("http://www.reddit.com/user/"+username)).read()).lower()
            loc = response.find('<span class="age">')
            age = response[loc+31:loc+42]
	    age = age[:age.find("<")]
            if "year" in age or "month" in age or "years" in age or "months" in age:
		open("reddit2.txt", "a").write(username.lower()+"\n")
                return "reddit"
        return False

# Kicks a user (hurr-durr)
def kick(username):
    server.stdin.write(optoken+"kick "+username+"\n")

# Beats to fList and keeps statistics.  Also updates redditpublic.com in real-time.
def beat(players, beats, originalbeats):
    try:
        x = urllib2.urlopen("http://listcraft.minipenguin.com/beat.php","name=Reddit Public (Paid or Redditors Only Allowed)&max=32&server=reddit&wesite=redditpublic.com&public=true&port=9001&salt=9001&version=1&users="+str(len(players))+"&hash="+open("externalurl.txt").read()[41:]+"&motd=E-mail phil@linux.com for ban appeal&players="+','.join(players))
    except:
	if debug:
	    print "failed to beat to flist"
	else:
	    pass
    if len(players) == 0:
        token = "No players are playing on Reddit Public.  Be the first!"
    elif len(players) >1:
	if len(players) != 2:
	   comma = ","
	else:
	   comma = ""
	token = ", ".join(players[:-1])+comma+" and "+players[-1]+" are playing on Reddit Public.  Join them!"
    else:
	token = "".join(players)+" is playing on Reddit Public.  Join him!"
    open("../nerdnu/blog/players.php", "w").write("document.write('"+token+"');")
    if debug:
         print "Beating to flist.  Player(s): "+" ".join(players)
    beats[0] = beats[0]+1
    print str(beats[0]) + " beats in "+str(time.time()-originalbeats)+" seconds."
    return time.time()

# Writes out a list to a file
def writeout(lest, fele):
    open(fele, "w")
    [x.reverse() for x in lest]
    lest.sort(reverse=True)
    for x in lest:
	if len(x) == 2:
	    open(fele, "a").write(str(x[1])+"-"+str(x[0])+"\n")

# Gets the karma for a given user.
def getkarma(user):
    try:
        karma = open("rep.txt").read().splitlines()
        for x in karma:
    	    x = x.split("-")
	    if x[0].lower() == user.lower():
	        return x[1]
	return "No"
    except:
	return ""

# Prints a user's join message
def printjoin(user):
   time.sleep(.2)
   #server.stdin.write(optoken+"say |&2Welcome to Reddit Public, "+user+"!\n")
   #for x in open('joinmessage.txt').read().splitlines():
   #     server.stdin.write(optoken+"say "+x+"\n")
   server.stdin.write(optoken+"deop "+user+"\n")
   server.stdin.write(optoken+"op "+user+"\n")
   messages = open("messages.txt").read().splitlines()
   messages = [x.split("-",1) for x in messages]
   for x in messages:
      if x[0].lower().strip() == user.lower().strip():
	server.stdin.write(optoken+"say You have &5new messages.&k Use !inbox to view.\n")
	break

#______________________________________________________________________________________________________________________________________________________________________________________________________
# No more functions :).

# Start your engines.
server = subprocess.Popen(("java","-Xmx512M","-Xms512M", "-cp", "minecraft-server.jar", "com.mojang.minecraft.server.MinecraftServer"), stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
plugin = StdinThread(server)
plugin.start()

while True:
    output = server.stderr.readline().strip()
    if not output:
        continue
    if not "unknown source" in output.lower():
        log.write("%s\n" % output)
    log.flush()
    print output

    # Recent Chat Logging
    while len(chatlog) >= 50:
        chatlog.remove(chatlog[0])
    if (chatregexp.match(output) or adminregexp.match(output)) and (not "setPrawns" in output and not "solidarity" in output): 
        chatlog.append(output)
        logfile = open("../nerdnu/blog/chatlog.js", "w").write("")
        logfile = open("../nerdnu/blog/chatlog.js", "a")
        logfile.write('document.write("\n')
        for x in chatlog:
	    x.replace('"',"'")
	    logfile.write(x+"\n")
        logfile.write('");')
	logfile.close()


    # Volatile Variables
    jointoken = ""
    done = False

    try:
        # Check if confirmation needed to keep server open
	if time.time() - lopen >= 600 and opened and not cneeded:
	    server.stdin.write(optoken+"say *&4An admin must type /open within 1 min to confirm being open.\n")
	    lconf = time.time()
	    cneeded = True
	    
        # Begin the closing process
	if time.time() - lconf >= 60 and cneeded:
            close()
            lopen = 0
            opened = False
            closeit = time.time()
	    cneeded = False

	# Kick the frees!
	if (time.time() - closeit >= 60):
	    server.stdin.write(optoken+"say *&4All free users are being kicked.\n")
	    for x in players:
		    if not isredditor(x) and x:
		        kick(x)
	    closeit = time.time() * time.time() * time.time()
        
        # Beat if no beat was made in the last 200 seconds
        if time.time() - lbeat >= 200:
            lbeat = beat(players, beats, fbeat)
        # Welcome message / Free Kick on Join
        if loginregexp.match(output):
            try:
                redditor = isredditor(loginregexp.match(output).groups()[0])
	    except:
            	kick(loginregexp.match(output).groups()[0])
            if not redditor and not opened:
                kick(loginregexp.match(output).groups()[0])
                #server.stdin.write(optoken+"say "+(loginregexp.match(output).groups()[0])+" has been kicked (Not redditor or premium).\n")
            else:
                if not redditor:
                    jointoken = "&2Free"
                elif redditor == "admin":
                    jointoken = "&4Admin"
		elif redditor == "reddit":
		    jointoken = "Redditor"
                else:
                    jointoken = "&8Premium"
		userIp = output.split()[1][1:]
		ip = urllib2.urlopen("http://ipinfodb.com/ip_query_country.php?ip="+userIp).read()
		message = optoken+"say "+loginregexp.match(output).groups()[0]+" connected ("+jointoken+"&e) (&3"+getkarma(loginregexp.match(output).groups()[0])+" Karma&e) &2("
		if len(loginregexp.match(output).groups()[0]) + len(ip[ip.find("<CountryName>")+13:ip.find("</CountryName>")]) < 17:
		    message = message+ip[ip.find("<CountryName>")+13:ip.find("</CountryName>")]+")\n"
		else:
		   message = message+ip[ip.find("<CountryCode>")+13:ip.find("</CountryCode")]+")\n" 
                #server.stdin.write(optoken+"say %s connected (%s&e) (&3%s Karma&e) (&3%s).\n" % (loginregexp.match(output).groups()[0], jointoken, getkarma(loginregexp.match(output).groups()[0]),ip[ip.find("<CountryName>")+13:ip.find("</CountryName>")]))
		server.stdin.write(message)
                players.append(loginregexp.match(output).groups()[0])
                printjoin(loginregexp.match(output).groups()[0])
                lbeat = beat(players, beats, fbeat)

        # Website / fList Update on Disconnect
        elif disconnectregexp.match(output):
	    if disconnectregexp.match(output).groups()[0] in players:
                players.remove(disconnectregexp.match(output).groups()[0])
            lbeat = beat(players, beats, fbeat)
                                      
        # Begin Admin Command Block
        elif adminregexp.match(output):
            adminee = adminregexp.match(output).groups()[0]
            command = adminregexp.match(output).groups()[1]

            if command.split()[0].lower() == "me":
                server.stdin.write(optoken+"say *&3"+adminee+" "+" ".join(command.split(" ")[1:])+"\n")
            
            elif isadmin(adminee):

                if command.split()[0].lower()  == "trust":
                    if len(command.split()) == 2:
                        open("redditors.txt", "a").write(command.split()[1].lower()+"\n")
                        server.stdin.write(optoken+"say User "+command.split()[1]+" trusted.\n")
                    else:
                        server.stdin.write(optoken+"say Trust takes one arg.  !help trust for more.\n")
                    
                                   
                elif command.lower().strip() == "open":
                    lopen = time.time()
                    opened = True
                    cneeded = False
                    server.stdin.write(optoken+"say *&4Server Opened to Free Users\n")
                    closeit = time.time() * time.time() * time.time()
                elif command.lower().strip() == "close":
                    close()
                    lopen = 0
                    opened = False
                    closeit = time.time()


                # Forced re-implementation of vanilla commands w/ token.
                
                elif command.split()[0].lower() == "op":
                    if len(command.split()) == 2:
                        open("admins2.txt", "a").write(command.split()[1].lower()+"\n")
                        server.stdin.write(optoken+"say User "+command.split()[1].lower()+" is now an Admin.\n")
                    else:
                        server.stdin.write(optoken+"say Op takes one argument.\n")
                        
                elif command.split()[0].lower() == "ban":
                    if len(command.split()) == 2:
                        server.stdin.write(optoken+"ban "+command.split()[1].lower()+"\n")
                    else:
                        server.stdin.write(optoken+"say Ban takes one argument.\n")
                        
                elif command.split()[0].lower() == "say":
                    server.stdin.write(optoken+"say"+command[command.find("say")+3:]+"\n")

                elif command.split()[0].lower() == "unban":
                    if len(command.split()) == 2:
                        server.stdin.write(optoken+"unban "+command.split()[1].lower()+"\n")
                    else:
                        server.stdin.write(optoken+"say Unban takes one argument.\n")

		elif command.split()[0].lower() == "deop":
		    done = 0
		    if len(command.split()) == 2:
			ops = open("admins2.txt").read().splitlines()
			for x in ops:
			    try:
			        if x.lower().strip() == command.split()[1].lower().strip():
				    ops.remove(x)
				    done = 1
			    except:
				pass
			if done:
			    admins = open("admins2.txt", "w").write("")
			    admins = open("admins2.txt", "a")
			    for x in ops:
				admins.write(x+"\n")
			    admins.close()
			else:
			    server.stdin.write(optoken+"say User not found in OP list.\n")
		    else:
			server.stdin.write(optoken+"say Deop takes on argument.\n")
		    
	# Begin Mute Block
	elif muteregexp.match(output):
	    server.stdin.write(optoken+"say "+muteregexp.match(output).groups()[0]+" was muted for 8 seconds.\n")

        # Begin Chat Block
        elif chatregexp.match(output):
            chatee = chatregexp.match(output).groups()[0]
            message = chatregexp.match(output).groups()[1]

            # Check for and execute triggers ;).
            triggers = open("triggers.txt").read().splitlines()
            triggers = [x.split(",",1) for x in triggers]
            for z in triggers:
                if not done:
                    begit = False
                    if (z[0][:3] == "##B") and (message[:len(z[0])-3].lower()==z[0][3:]):
                        begit = True
                    if z[0] in message and z[:3] != "##B" or begit:
                        try:
                            if "##!" in z[1]:
                                newline = z[1].split("##!")
                                for x in newline:
                                    server.stdin.write(optoken+"say RBot:&g " + x+"\n")
                                    done = True
                            else:
                                server.stdin.write(optoken+"say RBot:&g " + z[1]+"\n")
				done = True 
                        except:
                            print "Trigger failure." # No exceptions here.  I don't like them :)

            if "!who" in message.lower():
		admins = []
		prem = []
		reddit = []
		free = []
                for x in players:
		    if isredditor(x) == "admin":
			admins.append(x)
		    elif isredditor(x) == "premium":
			prem.append(x)
		    elif isredditor(x) == "reddit":
			reddit.append(x)
		    else:
			free.append(x)

		if len(admins) > 0:
		    coolList(admins, "&4", "Admins: ", 55) 
		if len(prem) > 0:
		    coolList(prem, "&8", "Premium Users: ",50)
		if len(reddit) > 0:
		    coolList(reddit, "", "Redditors: ", 52)
		if len(free) > 0:
		    coolList(free, "&2", "Frees: ", 58)

            elif "!time" in message.lower():
                server.stdin.write(optoken+"say RBot:&g EST (reddit) Time: "+str(time.asctime(time.localtime(time.time()))) +"\n")

            elif "!calendar" in message.lower():
                server.stdin.write(optoken+"say Current Calendar:\n")
                x = calendar.month(time.localtime(time.time())[0],time.localtime(time.time())[1]).split("\n")
		for x in x:
		    server.stdin.write(optoken+"say "+x+"\n")

            elif "!freetrust" in message.lower():
                if len(message.split(" ")) == 2 and message.split()[0].lower()=="!freetrust" and len(message.split()[1].split("@")) == 2:
                    server.stdin.write(optoken+"say Request sent.  Expect to hear back within 24hrs.\n")
                    open("treq.txt", "a").write(message)
                else:
                    server.stdin.write(optoken+"say Type !help freetrust for help.\n")

            elif "!profile" in message.lower():
                match = message.split(" ")
                if len(match) != 2:
                    server.stdin.write(optoken+"say Improper usage of !profile.\n")
                else:
		    ecsta = False
                    token2 = isredditor(match[1])
                    if token2 == "premium":
                        token2 = "premium user"
                    elif not token2:
			playel = [x.lower() for x in players]
			if match[1].lower() in playel:
			    token2 = "free user"
			else:
                            token2 = "non-user"
                    elif token2 == "reddit":
                        token2 = "redditor"
                    server.stdin.write(optoken+"say User "+match[1]+" is a(n) "+token2+"\n")
                    server.stdin.write(optoken+"say User has "+getkarma(match[1])+" Karma\n")
		    status = open("status.txt").read().splitlines()
		    status = [x.split("-") for x in status]
		    for x in status:
			if x[0].lower() == match[1].lower():
			    server.stdin.write(optoken+"say Status: &4"+x[1]+"\n")
			    ecsta = True
		    if not ecsta:
			server.stdin.write(optoken+"say No status. Use !help status for instructions.\n")

	    elif "!status" in message.lower():
		done = False
		if message.split()[0] == "!status" and len(message.split()) >= 2:
		   status = open("status.txt").read().splitlines()
		   status = [x.split("-") for x in status]
		   for x in range(len(status)):
		        if chatee.lower() == status[x][0].lower():
			    status[x][1] = message[message.find("!status")+8:]
			    done = True
		   if not done:
		       status.append([chatee, message[message.find("!status")+8:]])
		   statusl = open("status.txt", "w")
		   for x in status:
			statusl.write(x[0]+"-"+x[1]+"\n")
		   statusl.close()
		   server.stdin.write(optoken+"say Status set to: "+message[message.find("!status")+8:]+"\n")

	    elif "!msg" in message.lower():
		if len(message.split()) > 2:
		    if len(message.split(" ",2)[2]) + 16 + len(chatee) > 64:
			server.stdin.write(optoken+"say Message too long. Try again.\n")
		    else:
		        message = message.split(" ",2)
	    	        open("messages.txt","a").write(message[1]+"-"+chatee+": "+message[2]+" ("+" ".join(time.asctime(time.localtime(time.time())).split()[1:-1])+")\n")
	   	        server.stdin.write(optoken+"say "+message[1]+" will be notified of message on next logon.\n")		
		else:
		   server.stdin.write(optoken+"say See !help msg for use info.\n")
	    elif message.lower().split()[0] =="!inbox":
		messaged = False
		messages = open("messages.txt").read().splitlines()
		messages = [x.split("-",1) for x in messages]
		if len(message.split()) == 1:
		    for x in messages:
		        if x[0].lower().strip() == chatee.lower().strip():
			    server.stdin.write(optoken+"say "+x[1]+"\n")
			    messaged = True
		elif len(message.split()) == 2:
		    if message.split()[1].lower() == "clear":
		        mfile = open("messages.txt", "w").write("")
		        mfile = open("messages.txt", "a")
		        for x in messages:
			    if len(x) == 2 and not (x[0].lower().strip() == chatee.lower().strip()):
			        mfile.write(x[0]+"-"+x[1]+"\n")
		        mfile.close()
			server.stdin.write(optoken+"say Inbox cleared.\n")
		if messaged:
		    server.stdin.write(optoken+"say Use !inbox clear to clear your inbox.\n")
		elif len(message.split()) == 1:
		    server.stdin.write(optoken+"say No messages.\n")

	    elif message.lower() == "!list":
		siteList = open("../redditbot/sites.txt").read().splitlines()
		siteList = [x.split("^")[0] for x in siteList]
		swapList = []
		theList = []
		for x in siteList:
		    if len(", ".join(swapList))+len(x) > 62:
			theList.append(swapList)
			swapList = [x]
		    else:
			swapList.append(x)
		theList.append(swapList)
		print theList
		server.stdin.write(optoken+"say Available sites (Use !go [site] to go):\n")
		for x in theList:
		    server.stdin.write(optoken+"say "+", ".join(x)+"\n")

            # Karma block.
            elif "!karma" in message.lower():
                message = message.split()
                if not (len(message) == 3 and (message[1] == "add" or message[1] == "count")):
                    server.stdin.write(optoken+"say Type !help karma for usage instructions.\n")
                else:
                    while True:
			breaki = False
                        added = False
                        if message[1] == "count":
                            server.stdin.write(optoken+"say User has "+getkarma(message[2])+" Karma\n")
			    break
                        elif message[1] == "add":
                            # Get and parse the list of reps and last add times
                            rep = open("rep.txt").read().split("\n")
                            times = open("unixtime.txt").read().split("\n")
                            rep = [x.split("-") for x in rep]
                            times = [x.split("-") for x in times]
			    user = message[2].strip()
                            adduser = output[output.find(" ")+2:output.find("says:")]
                            for x in times:
                                if adduser.lower() == x[0].lower() and (time.time() - float(x[1]) <=86400) and user.lower() == x[2].lower().strip():
                                    server.stdin.write(optoken+"say You must wait another "+str((86400-(time.time()-float(x[1])))/3600)[:4]+" hrs to rep "+user+".\n")
                                    breaki = True
			    if breaki:
				break
                            if adduser.lower().strip() == message[2].lower().strip():
                                server.stdin.write(optoken+"say You can't add karma to yourself.\n")
                                break
                            for x in times:
                                if adduser.lower().strip() == x[0].lower().strip() and user.lower() == x[2].lower().strip():
                                    x[1] = time.time()
                                    added = True
				    x[2] = user.lower()
                            if not added:
                                times.append([adduser, time.time(), user.lower()])
                            for x in rep:
                                if x[0].lower().strip() == message[2].lower().strip():
                                    x[1] = int(x[1])+1
                                    server.stdin.write(optoken+"say One karma added to "+x[0]+".\n")
                                    breaki = True
                            if breaki:
				break
			    rep.append([message[2], 1])
                            server.stdin.write(optoken+"say "+message[2]+" added to list w/ 1 karma.\n")
                            break
		    if message[1] == "add":
			open("unixtime.txt","w").write("")
			for x in times:
			    if len(x) > 1:
				print "writing out",x
			        open("unixtime.txt", "a").write(str(x[0])+"-"+str(x[1])+"-"+str(x[2])+"\n")
			for x in range(len(rep)):
			   if len(rep[x])>2:
				rep[x] = [rep[x[0]],int(rep[x][1])]
                        writeout(rep, "rep.txt")
			x = open("unixtime.txt","a")
			x.close()
                            
                

            # Reporting at work.
            elif "!report" in message.lower():
                open("../nerdnu/blog/reports.txt", "a").write(output+"\n")
                server.stdin.write(optoken+"say Thank you for your report.  We will investigate the incident.\n")
                                                
                    
                                                
    # Oh no, shutdown!
    except KeyboardInterrupt:
        print "Shutting down..."
        log.write("Shutting down...\n")
        log.flush()
        plugin.stop = True
        sys.exit()
        quit()
